import re

import requests
import select

import config
from urllib.parse import urljoin, urlparse
from bs4 import BeautifulSoup as bs


class FileUpload:
    def __init__(self, url, session):
        self.session = session
        self.url = url
        self.file_form_details = []
        self.soup = bs()

    def get_file_form(self):

        try:
            soup = bs(self.session.get(self.url).content, "html.parser")
            forms = soup.find_all("form")
        except:
            print('[x]获取表单时出现错误')
            return None
        for form in forms:
            # 查找所有file类型，仅保存有file类型的表单
            files = form.find_all("input", type='file')
            if not files:
                continue
            details = {'upload_name': []}
            # 保存所有file的name
            for file in files:
                details['upload_name'].append(file.attrs.get('name'))

            # 获取action
            try:
                action = form.attrs.get("action").lower()
            except:
                action = None

            # 获取其余表单信息
            inputs = []
            for input_tag in form.find_all(["input", "select", "textarea"]):
                input_type = input_tag.attrs.get("type", "text")
                input_name = input_tag.attrs.get("name")
                input_value = input_tag.attrs.get("value", "")
                inputs.append({"type": input_type, "name": input_name, "value": input_value})
            details["action"] = action
            details["inputs"] = inputs
            self.file_form_details.append(details)

    def send_form(self):
        for form_details in self.file_form_details:
            data = {}
            for input_tag in form_details["inputs"]:
                if input_tag["value"] or input_tag["type"] == "hidden":
                    # 有隐藏数据(例如TOKEN),则直接提交
                    try:
                        data[input_tag["name"]] = input_tag["value"]
                    except:
                        pass
                elif input_tag["type"] != "submit" and input_tag["type"] != "file":
                    # all others except submit, use some junk data with special character
                    data[input_tag["name"]] = "test"
            # join the url with the action (form request URL)
            url = urljoin(self.url, form_details["action"])
            with open(config.file_upload_payload, 'rb') as f:
                file = {form_details['upload_name'][0]: ("upload_file.html", f.read(), 'image/png')
                        # key：value（上传图片时有时附带其他字段时，选填）
                        }
                self.session.post(url, files=file, data=data)
                res = self.session.get(url)
                self.soup = bs(res.content, "html.parser")

    def find_file(self):
        # 先找当前页面的图片是否有文件
        images = self.soup.find_all('img')
        for img in images:
            s_url = img.get('src', "html.parser")
            if s_url is not None:
                s_url = str(s_url).strip()
                if re.search(r'File Upload Vulnerability Detected!', self.session.get(urljoin(self.url, s_url)).text):
                    return True
        return False

    def run(self):
        self.get_file_form()
        self.send_form()


def scan(url, session):
    result = []

    f = FileUpload(url=url, session=session)
    f.run()
    if f.find_file():
        print(f"\033[33m[!]发现【文件上传漏洞】在{url}页面上 \033[0m")
        result.append({
            'vun_type': "fileupload",
            "status": '高危',
            'url': url,
            "description": "文件上传漏洞",
            "payload": config.file_upload_payload
        })
    else:
        print("[+]No Vul Dectected on",url)
    return result


if __name__ == '__main__':
    session = requests.session()
    if scan('http://127.0.0.1:8000', session):
        print('dectected!')
